import Foundation
import UIKit
import Display
import ComponentFlow
import SwiftSignalKit
import GZip
import AppBundle
import LegacyComponents
import AvatarNode
import AccountContext
import TelegramCore
import AnimationCache
import MultiAnimationRenderer
import EmojiStatusComponent

class EmojiHeaderComponent: Component {
    let context: AccountContext
    let animationCache: AnimationCache
    let animationRenderer: MultiAnimationRenderer
    let placeholderColor: UIColor
    let accentColor: UIColor
    let fileId: Int64
    let file: TelegramMediaFile?
    let isVisible: Bool
    let hasIdleAnimations: Bool
        
    init(
        context: AccountContext,
        animationCache: AnimationCache,
        animationRenderer: MultiAnimationRenderer,
        placeholderColor: UIColor,
        accentColor: UIColor,
        fileId: Int64,
        file: TelegramMediaFile? = nil,
        isVisible: Bool,
        hasIdleAnimations: Bool
    ) {
        self.context = context
        self.animationCache = animationCache
        self.animationRenderer = animationRenderer
        self.placeholderColor = placeholderColor
        self.accentColor = accentColor
        self.fileId = fileId
        self.file = file
        self.isVisible = isVisible
        self.hasIdleAnimations = hasIdleAnimations
    }
    
    static func ==(lhs: EmojiHeaderComponent, rhs: EmojiHeaderComponent) -> Bool {
        return lhs.placeholderColor == rhs.placeholderColor && lhs.accentColor == rhs.accentColor && lhs.fileId == rhs.fileId && lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations
    }
    
    final class View: UIView, ComponentTaggedView {
        final class Tag {
        }
        
        func matches(tag: Any) -> Bool {
            if let _ = tag as? Tag {
                return true
            }
            return false
        }
        
        private var _ready = Promise<Bool>(true)
        var ready: Signal<Bool, NoError> {
            return self._ready.get()
        }
        
        weak var animateFrom: UIView?
        var sourceRect: CGRect?
        weak var containerView: UIView?
        
        let statusView: ComponentHostView<Empty>
        
        private var hasIdleAnimations = false
        
        override init(frame: CGRect) {
            self.statusView = ComponentHostView<Empty>()
        
            super.init(frame: frame)
        
            self.statusView.isHidden = true
            self.addSubview(self.statusView)
                        
            self.disablesInteractiveModalDismiss = true
            self.disablesInteractiveTransitionGestureRecognizer = true
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        var scheduledAnimateIn = false
        override func didMoveToWindow() {
            super.didMoveToWindow()
            
            if self.scheduledAnimateIn {
                self.animateIn()
                self.scheduledAnimateIn = false
            }
        }
         
        func animateIn() {
            guard let animateFrom = self.animateFrom, var containerView = self.containerView else {
                return
            }
            
            guard let _ = self.window else {
                self.scheduledAnimateIn = true
                return
            }
                        
            self.statusView.isHidden = false
            if containerView.subviews.count > 1 && containerView.subviews[1].subviews.count > 1 {
                let candidateView = containerView.subviews[1].subviews[1]
                if !(candidateView is UIVisualEffectView) {
                    containerView = candidateView
                }
            }
            
            let initialPosition = self.statusView.center
            let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView)
            
            var sourceOffset: CGPoint = .zero
            if let sourceRect = self.sourceRect {
                sourceOffset = CGPoint(x: sourceRect.center.x - animateFrom.frame.width / 2.0, y: 0.0)
            }
            let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: sourceOffset.x, dy: sourceOffset.y)

            containerView.addSubview(self.statusView)
            self.statusView.center = targetPosition
            
            animateFrom.alpha = 0.0
            self.statusView.layer.animateScale(from: 0.24, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.linear.rawValue)

            self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
            
            Queue.mainQueue().after(0.55, {
                self.statusView.layer.removeAllAnimations()
                self.addSubview(self.statusView)
                self.statusView.center = initialPosition
            })
            
            Queue.mainQueue().after(0.4, {
                animateFrom.alpha = 1.0
            })
            
            self.animateFrom = nil
            self.containerView = nil
        }
        
        func update(component: EmojiHeaderComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
            self.hasIdleAnimations = component.hasIdleAnimations
            
            let size = self.statusView.update(
                transition: .immediate,
                component: AnyComponent(EmojiStatusComponent(
                    context: component.context,
                    animationCache: component.animationCache,
                    animationRenderer: component.animationRenderer,
                    content: .animation(
                        content: component.file.flatMap { .file(file: $0) } ?? .customEmoji(fileId: component.fileId),
                        size: CGSize(width: 100.0, height: 100.0),
                        placeholderColor: component.placeholderColor,
                        themeColor: component.accentColor,
                        loopMode: .forever
                    ),
                    isVisibleForAnimations: true,
                    action: nil
                )),
                environment: {},
                containerSize: CGSize(width: 96.0, height: 96.0)
            )
            self.statusView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - size.width) / 2.0), y: 63.0), size: size)
            
            if let _ = component.file {
                self.statusView.isHidden = false
            }
            
            return availableSize
        }
    }
    
    func makeView() -> View {
        return View(frame: CGRect())
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, transition: transition)
    }
}

private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosition: CGPoint, elevation: CGFloat) -> [CGPoint] {
    let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - elevation)
    
    let x1 = sourcePoint.x
    let y1 = sourcePoint.y
    let x2 = midPoint.x
    let y2 = midPoint.y
    let x3 = targetPosition.x
    let y3 = targetPosition.y
    
    var keyframes: [CGPoint] = []
    if abs(y1 - y3) < 5.0 && abs(x1 - x3) < 5.0 {
        for i in 0 ..< 10 {
            let k = CGFloat(i) / CGFloat(10 - 1)
            let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
            let y = sourcePoint.y * (1.0 - k) + targetPosition.y * k
            keyframes.append(CGPoint(x: x, y: y))
        }
    } else {
        let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
        let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
        let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
        
        for i in 0 ..< 10 {
            let k = CGFloat(i) / CGFloat(10 - 1)
            let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
            let y = a * x * x + b * x + c
            keyframes.append(CGPoint(x: x, y: y))
        }
    }
    
    return keyframes
}
